using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public class MobileSDRAM : Component
    {

        public NodeVector RamAddr; // ok
        public NodeVector RamDM;  // ok, 0
        public NodeVector RamWE;  // ok
        public NodeVector RamClkE; // ok
        public NodeVector RamCS; // ok
        public NodeVector RamCAS; // ok
        public NodeVector RamRAS; // ok
        public NodeVector RamClk; // ok
        public NodeVector RamBA; //ok
        public NodeVector RamDOut; // ok
        public NodeVector RamDIn; //ok
        public NodeVector RamDT;  // ok

        private NodeVector SysClock;
        public NodeVector SysReset; //ok
        public NodeVector SysDataIn; //ok
        public NodeVector SysDataOut; //ok
        public NodeVector SysAddr; // ok
        public NodeVector SysCmd; // ok
        public NodeVector SysBusy; // okd

        ClockComponent cClock;

        public EncodeMap CommandMap = new EncodeMap();

        public MobileSDRAM(Component poParentComponent, string psName, int piAddressBits, int piDataBits, int piBanks, int piDataMaskBits, ClockComponent pcClock) 
            : base(poParentComponent,psName)
        {
            RamAddr = this.CreateNodeVector("RamAddr", piAddressBits);
            RamDM = this.CreateNodeVector("RamDM", piDataMaskBits);
            RamWE = this.CreateNodeVector("RamWE", 1);
            RamClkE = this.CreateNodeVector("RamClkE", 1);
            RamCS = this.CreateNodeVector("RamCS", 1);
            RamCAS = this.CreateNodeVector("RamCAS", 1);
            RamRAS = this.CreateNodeVector("RamRAS", 1);
            RamClk = this.CreateNodeVector("RamClk", 1);
            RamBA = this.CreateNodeVector("RamBA", piBanks);
            RamDOut = this.CreateNodeVector("RamDOut", piDataBits);
            RamDIn = this.CreateNodeVector("RamDIn", piDataBits);
            RamDT = this.CreateNodeVector("RamDT", piDataBits);
        
            SysClock = this.CreateNodeVector("SysClock",1);
            SysReset = this.CreateNodeVector("SysReset",1);
            SysDataIn = this.CreateNodeVector("SysDataIn",piDataBits);
            SysDataOut = this.CreateNodeVector("SysDataOut", piDataBits);
            SysCmd = this.CreateNodeVector("SysCmd", 3);
            SysAddr = this.CreateNodeVector("SysAddr",piAddressBits * 2 - 4);
            SysBusy = this.CreateNodeVector("SysBusy", 1);

            cClock = pcClock;
            SysClock.Connection = cClock.ClockOut;
            
            CommandMap.AddCode("IDLE");
            CommandMap.AddCode("DEEP_PD");
            CommandMap.AddCode("AUTO_RFS");
            CommandMap.AddCode("PRECHARGE");
            CommandMap.AddCode("LOAD_MR");
            CommandMap.AddCode("READ");
            CommandMap.AddCode("WRITE");
        }

        public override void GenerateStructure()
        {
            Decoder dcCommand = new Decoder(this, "decode_sys_cmd", 3, 8);
            dcCommand.EncodeMap = CommandMap;
            dcCommand.InputNodes.Connection = SysCmd;

            ClockComponent cInvClock = new ClockSink(this, "sram_clk");
            cInvClock.ClockIn.Connection = Operators.NOT(this, cClock.ClockOut);
            cInvClock.ClockOut.Connection = RamClk;


            StateMachine stmControl = new StateMachine(this, "state_controller", cClock);
            stmControl.AddState("RESET");            
            stmControl.AddState("IDLE");
            stmControl.AddState("AUTO_RFS_ST");
            stmControl.AddState("DPD_ST");
            stmControl.AddState("PRECHRG_ST");
            stmControl.AddState("LOAD_MR_ST");
            stmControl.AddState("ACTIVE_ST");
            stmControl.AddState("WAIT_READ_ST");
            stmControl.AddState("WAIT_WRITE_ST");
            stmControl.AddState("READ_ST");
            stmControl.AddState("WRITE_ST");
            stmControl.AddState("CAS_DELAY_ST");
            stmControl.AddState("WRITE_DATA_ST");
            stmControl.AddState("READ_DATA_ST");
            stmControl.AddState("WAIT_TWR");
            stmControl.AddState("WAIT_TRP");

            stmControl.ResetIn.Connection = SysReset;
            stmControl.SetDefaultState("RESET");

            stmControl.StateTransition("RESET", "IDLE");
            stmControl.StateTransition("IDLE", dcCommand.BitByName(this,"PRECHARGE"), "PRECHRG_ST");
            stmControl.StateTransition("PRECHRG_ST",  "IDLE");
            stmControl.StateTransition("IDLE", dcCommand.BitByName(this,"LOAD_MR"), "LOAD_MR_ST");
            stmControl.StateTransition("LOAD_MR_ST", "IDLE");
            stmControl.StateTransition("IDLE", dcCommand.BitByName(this,"DEEP_PD"), "DPD_ST");
            stmControl.StateTransition("DPD_ST", dcCommand.BitByName(this,"WAKE_UP"), "IDLE");
            stmControl.StateTransition("DPD_ST", Operators.NOT(this, dcCommand.BitByName(this,"WAKE_UP")), "DPD_ST");

            stmControl.StateTransition("IDLE", Operators.OR(this,dcCommand.BitByName(this,"WRITE"),dcCommand.BitByName(this,"READ")), "ACTIVE_ST");
            stmControl.StateTransition("ACTIVE_ST", dcCommand.BitByName(this, "READ"), "WAIT_READ_ST");
            stmControl.StateTransition("WAIT_READ_ST", "READ_ST");
            stmControl.StateTransition("READ_ST", "CAS_DELAY_ST");
            stmControl.StateTransition("READ_DATA_ST", "IDLE");

            
            stmControl.StateTransition("ACTIVE_ST", dcCommand.BitByName(this, "WRITE"), "WAIT_WRITE_ST");
            stmControl.StateTransition("WAIT_WRITE_ST", "WRITE_ST");
            stmControl.StateTransition("WRITE_ST", "WRITE_DATA_ST");
            stmControl.StateTransition("WRITE_DATA_ST", "WAIT_TWR");
            stmControl.StateTransition("WAIT_TWR", "WAIT_TRP");
            stmControl.StateTransition("WAIT_TRP", "IDLE");
            
            NodeVector cZero = Operators.CONST(this,0,1);
            NodeVector cOne = Operators.CONST(this,1,1);

            AssignmentMux aClkE = new AssignmentMux(this, "assign_clk_e", 1, cOne);
            aClkE.AddAssignment(stmControl.StateCurrent("DPD_ST"), cZero);
            aClkE.RegisterOutput(cClock, false);
            RamClkE.Connection = aClkE.OutputNodes;

            RamCS.Connection = cZero;

            AssignmentMux aRamRAS = new AssignmentMux(this, "assign_ram_ras", 1, cOne);
            aRamRAS.AddAssignment(Operators.OR(this,stmControl.StateCurrent("PRECHRG_ST"),stmControl.StateCurrent("RFSH_ST"),stmControl.StateCurrent("LOAD_MR_ST"),stmControl.StateCurrent("ACTIVE_ST")),cZero);
            aRamRAS.RegisterOutput(cClock, false);
            RamRAS.Connection = aRamRAS.OutputNodes;


            AssignmentMux aRamCAS = new AssignmentMux(this, "assign_ram_cas", 1, cOne);
            aRamCAS.AddAssignment(Operators.OR(this,stmControl.StateCurrent("RFSH_ST"),stmControl.StateCurrent("LOAD_MR_ST"),stmControl.StateCurrent("READ_ST"),stmControl.StateCurrent("WRITE_ST")),cZero);
            aRamCAS.RegisterOutput(cClock, false);
            RamCAS.Connection = aRamCAS.OutputNodes;

            AssignmentMux aRamWE = new AssignmentMux(this, "assign_ram_we", 1, cOne);
            aRamWE.AddAssignment(Operators.OR(this,stmControl.StateCurrent("PRECHRG_ST"),stmControl.StateCurrent("LOAD_MR_ST"),stmControl.StateCurrent("DPD_ST"),stmControl.StateCurrent("WRITE_ST")),cZero);
            aRamWE.RegisterOutput(cClock, false);
            RamWE.Connection = aRamWE.OutputNodes;

            Utility.SourceExtraBits(this,RamDM,0);

            ResetReg rSysAddress = new ResetReg(this,"sys_addr_reg",cClock,SysAddr.Nodes.Count,true,false,false);
            rSysAddress.ResetIn.Connection = SysReset;
            rSysAddress.ResetTo.Connection = Operators.CONST(this,0,SysAddr.Nodes.Count);
            rSysAddress.InputNodes.Connection = SysAddr;

            ResetReg rSysDataIn = new ResetReg(this,"sys_data_in_reg",cClock,SysDataIn.Nodes.Count,true,false,false);
            rSysDataIn.ResetIn.Connection = SysReset;
            rSysDataIn.ResetTo.Connection = Operators.CONST(this,0,SysDataIn.Nodes.Count);
            rSysDataIn.InputNodes.Connection = SysDataIn;

            ResetReg rSysDataOut = new ResetReg(this,"sys_data_out_reg",cClock,SysDataIn.Nodes.Count,true,false,false);
            rSysDataIn.ResetIn.Connection = SysReset;
            rSysDataIn.ResetTo.Connection = Operators.CONST(this,0,SysDataIn.Nodes.Count);

            ResetReg rSysCmd = new ResetReg(this,"sys_cmd_reg",cClock,SysCmd.Nodes.Count,true,false,false);
            rSysDataIn.ResetIn.Connection = SysReset;
            rSysDataIn.ResetTo.Connection = Operators.CONST(this,0,SysCmd.Nodes.Count);
            rSysDataIn.InputNodes.Connection = SysCmd;


            NodeVector nvNotReset = Operators.NOT(this,SysReset);
            NodeVector cAddrZeros = Operators.CONST(this,0,RamAddr.Nodes.Count);
            NodeVector nvRamAddr = SysAddr.Subset(this, 0, RamAddr.Width);
            NodeVector nvBA = SysAddr.Subset(this, SysAddr.Width - 2, SysAddr.Width - 1);
            
            NodeVector nvModeRegOut = this.CreateNodeVector("mode_reg_o",RamAddr.Nodes.Count);
            AssignmentMux aModeReg = new AssignmentMux(this, "mode_reg", RamAddr.Nodes.Count, nvModeRegOut);
            aModeReg.RegisterOutput(cClock, false);
            nvModeRegOut.Connection = aModeReg.OutputNodes;
            aModeReg.AddAssignment(SysReset,cAddrZeros);
            NodeVector nvWriteModeReg = Operators.AND(this,Operators.NOT(this,nvBA));
            aModeReg.AddAssignment(Operators.AND(this,nvNotReset,stmControl.StateCurrent("LOAD_MR_ST"),nvWriteModeReg),nvRamAddr);

            NodeVector nvPrechargeAddr = this.CreateNodeVector("precharge",RamAddr.Width);
            nvPrechargeAddr.ConnectSubset(nvPrechargeAddr.Width,nvPrechargeAddr.Width - 4,Operators.CONST(this,2,4),3,0);
            nvPrechargeAddr.ConnectSubset(nvPrechargeAddr.Width - 5,0,rSysAddress.OutputNodes.Subset(this,nvPrechargeAddr.Width,rSysAddress.OutputNodes.Width - 1),0,1);
            AssignmentMux aRamAddr = new AssignmentMux(this, "ram_addr_reg", RamAddr.Nodes.Count,nvPrechargeAddr);
            aRamAddr.RegisterOutput(cClock, false);
            aRamAddr.OutputNodes.Connection = RamAddr;
            aModeReg.AddAssignment(SysReset, cAddrZeros);
            aModeReg.AddAssignment(Operators.AND(this,nvNotReset, Operators.OR(this,stmControl.StateCurrent("LOAD_MR_ST"), stmControl.StateCurrent("ACTIVE_ST"))), nvRamAddr);


            NodeVector nvEModeRegOut = this.CreateNodeVector("ext_mode_reg_o", RamAddr.Nodes.Count);
            AssignmentMux aEModeReg = new AssignmentMux(this, "ext_mode_reg", RamAddr.Nodes.Count, nvEModeRegOut);
            aModeReg.RegisterOutput(cClock, false);
            nvEModeRegOut.Connection = aEModeReg.OutputNodes;
            aEModeReg.AddAssignment(SysReset, cAddrZeros);
            aModeReg.AddAssignment(Operators.AND(this,nvNotReset, stmControl.StateCurrent("LOAD_MR_ST"), Operators.NOT(this,nvWriteModeReg)), nvRamAddr);

            AssignmentMux aBA = new AssignmentMux(this, "ba_reg", 2, Operators.CONST(this, 0, 2));
            aBA.RegisterOutput(cClock, false);
            aBA.AddAssignment(Operators.OR(this,stmControl.StateCurrent("ACTIVE_ST"),stmControl.StateCurrent("READ_ST"),stmControl.StateCurrent("WRITE_ST"),stmControl.StateCurrent("PRECHRG_ST"),stmControl.StateCurrent("LOAD_MR_ST"),stmControl.StateCurrent("WRITE_DATA_ST")),nvBA);
            aBA.OutputNodes.Connection = RamBA;

            NodeVector nvDataOutEnable = this.CreateNodeVector("data_out_enable", 1);
            for (int iBit = 0; iBit > RamDT.Nodes.Count; iBit++)
            {
                nvDataOutEnable.ConnectSubset(RamDT, iBit, iBit);
            }

            AssignmentMux aOutputEnable = new AssignmentMux(this, "oe_reg", 1, cOne);
            aOutputEnable.AddAssignment(stmControl.StateCurrent("WRITE_ST"), cZero);
            aOutputEnable.OutputNodes.Connection = nvDataOutEnable;

            RegisterComponent rDataRead = new RegisterComponent(this, "data_read_reg", cClock, SysDataOut.Width, false, true);
            rDataRead.InputNodes.Connection = RamDIn;
            rDataRead.OutputNodes.Connection = SysDataOut;
            rDataRead.EnableNodes.Connection = stmControl.StateCurrent("READ_ST");

            RegisterComponent rDataOut = new RegisterComponent(this, "data_out_reg", cClock, SysDataOut.Width, true, true);
            rDataOut.InputNodes.Connection = SysDataIn;
            rDataOut.OutputNodes.Connection = RamDOut;
            rDataOut.EnableNodes.Connection = stmControl.StateCurrent("ACTIVE_ST");

            SysBusy.Connection = Operators.NOT(this, stmControl.StateCurrent("IDLE"));

        }
    }
}
